﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Preactor.ObjectModel.Entities
{
    using System.Diagnostics;

    using Preactor.ObjectModel.BaseTypes;
    using Preactor.ObjectModel.BaseTypes.MaterialControl;
    using Preactor.ObjectModel.Interfaces;
    using Preactor.ObjectModel.Interfaces.MaterialControl;

    public class MaterialControl
    {
        public MaterialControlStore OrderLinks { get; set; }

        public Dictionary<string, Order> Orders { get; set; }

        private PreactorObjectModel Pom { get; set; }

        public MaterialControl(PreactorObjectModel preactorObjectModel)
        {
            Pom = preactorObjectModel;
            OrderLinks = new MaterialControlStore(Pom);
            Orders = Pom.GetAllOrders().ToDictionary(x => x.OrderNumber, x => x);
        }

        public IEnumerable<IOperation> GetInternalSupplyOperations(IOperation operation, int maxLevel = 0)
        {
            var operationsFromSameOrder = new List<IOperation>();
            var operationsFromOtherOrders = new List<IOperation>();

            // Find supply operations in same order
            operationsFromSameOrder = GetSupplyOperationsFromSameOrder(operation);
            operationsFromOtherOrders = GetSupplyOperationsFromOtherOrders(operation);

            return operationsFromSameOrder.Union(operationsFromOtherOrders);
        }


        public List<IList<IOperation>> GetAllInternalSupplyOperations(IOperation operation, int maxLevel = 0)
        {
            var operations = new List<IList<IOperation>>();
            this.GetSupplyOperations(operation, operations, maxLevel);

            return operations;
        }

        public List<IList<IOperation>> GetAllInternalDemandOperations(IOperation operation)
        {
            var operations = new List<IList<IOperation>>();
            this.GetDemandOperations(operation, operations);

            return operations;
        }

        public List<IOperation> GetSupplyOperationsFromOtherOrders(IOperation operation)
        {
            return OrderLinks.GetInternalSupplyOperationsFor(operation).ToList();
        }

        public List<IOperation> GetDemandOperationsFromOtherOrders(IOperation operation)
        {
            return OrderLinks.GetInternalDemandOperationsFor(operation).ToList();
        }

        private void GetSupplyOperations(IOperation operation, List<IList<IOperation>> operations, int maxLevel = 0, int currentLevel = 0)
        {
            var internalOperations = this.GetSupplyOperationsFromSameOrder(operation);
            internalOperations.Add(operation);

            if (maxLevel == 0 || currentLevel < maxLevel)
            {
                currentLevel++;

                foreach (var internalOperation in internalOperations)
                {
                    // Get External Operations
                    var externalOperations = this.GetSupplyOperationsFromOtherOrders(internalOperation);

                    if (externalOperations.Any())
                    {
                        foreach (var externalOperation in externalOperations)
                        {
                            this.GetSupplyOperations(externalOperation, operations, maxLevel, currentLevel);
                        }

                        externalOperations.Add(internalOperation);
                        operations.Add(externalOperations);
                    }
                }
            }

            if (internalOperations.Count >= 1)
            {
                operations.Add(internalOperations);
            }
        }

        private void GetDemandOperations(IOperation operation, List<IList<IOperation>> operations)
        {
            var internalOperations = new List<IOperation>(){operation};
            internalOperations.AddRange(this.GetDemandOperationsFromSameOrder(operation));

            foreach (var internalOperation in internalOperations)
            {
                // Get External Operations
                var externalOperations = new List<IOperation>() { internalOperation };
                externalOperations.AddRange(this.GetDemandOperationsFromOtherOrders(internalOperation));

                if (externalOperations.Count > 1)
                {
                    foreach (var externalOperation in externalOperations.Where(x => x.Id.Value != operation.Id.Value))
                    {
                        this.GetDemandOperations(externalOperation, operations);
                    }

                    operations.Add(externalOperations);
                }
            }

            if (internalOperations.Count >= 1)
            {
                operations.Add(internalOperations);
            }
        }

        private List<IOperation> GetSupplyOperationsFromSameOrder(IOperation operation)
        {
            return  Orders[operation.OrderNumber].Operations.Where(x => x.OperationNumber < operation.OperationNumber).Select(x => x).ToList();
        }

        private List<IOperation> GetDemandOperationsFromSameOrder(IOperation operation)
        {
            var result = Orders[operation.OrderNumber].Operations.Where(x => x.OperationNumber > operation.OperationNumber).Select(x => x).ToList();
            return result;
        }
    }
}
